\documentclass[twoside,a4paper]{article}
\usepackage{geometry}
\geometry{margin=1.5cm, vmargin={0pt,1cm}}
\setlength{\topmargin}{-1cm}
\setlength{\paperheight}{29.7cm}
\setlength{\textheight}{25.3cm}

% useful packages.
\usepackage[UTF8]{ctex}	% 中文支持
\usepackage{listings} 	% 插入代码
\usepackage{xcolor} 	% 语法高亮
\usepackage{amsfonts}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsthm}
\usepackage{enumerate}
\usepackage{graphicx}
\usepackage{multicol}
\usepackage{fancyhdr}
\usepackage{layout}
\usepackage{diagbox} 	% 斜线表头
\usepackage{fontspec}   % 字体包

\lstset{
	columns=fixed,       
	numbers=left,                                        	% 在左侧显示行号
	numberstyle=\color[RGB]{120,120,141},                   % 设定行号格式
	basicstyle=\fontspec{Consolas}\small\color[RGB]{82,82,82},
	tabsize=4,
	frame=single,                                        	% 背景边框
	rulecolor=\color[RGB]{248,248,248},                  	% 边框颜色
	extendedchars=false,                                	% 解决换页问题
	backgroundcolor=\color[RGB]{248,248,248},            	% 设定背景颜色
	keywordstyle=\color[RGB]{214,50,128},                   % 设定关键字颜色
	identifierstyle=\color[RGB]{82,82,82},                  % 普通标识符颜色
	commentstyle=\it\color[RGB]{120,120,120},               % 设置代码注释的格式
	stringstyle=\rmfamily\slshape\color[RGB]{206,145,91},   % 设置字符串格式
	showstringspaces=false,                              	% 不显示字符串中的空格
	language=c++,                                        	% 设置语言
	escapeinside=``,                                     	% 逃逸字符
	xleftmargin=1em,                                     	% 边距设置
	xrightmargin=1em, 
	aboveskip=1em
}

\begin{document}
	
\pagestyle{fancy}
\fancyhead{}
\lhead{动画基础}
\chead{编程报告}
\rhead{\today}
	
{\centering\section*{编程报告}}

\section{项目介绍}
本项目实现动画基础部分 free-form deformation 算法的可视化，主要目标为实现两篇文献中的两个局部网格变形算法
\begin{itemize}
	\item 输入模型网格点，通过控制规则网格上的控制点移动来实现对局部或整个模型的变形；
	\item 同时在第二种算法的基础上实现单点控制，直接移动被绑定的物体上的点反求控制点移动，使得局部变形算法可以精确地将某一点变形至指定位置。
\end{itemize}
	
\subsection{项目分工}
项目成员：余文硕、金桉杰、行一凡。
\begin{itemize}
	\item 余文硕：前期项目框架讨论，查找变形模型素材，撰写项目报告；
	\item 金桉杰：前期项目框架讨论，FFD 算法构建实现，测试分析变形结果，撰写项目报告；
	\item 行一凡：前期项目框架讨论，项目框架和图形交互界面设计，查找变形模型素材，测试分析变形结果，撰写项目报告
\end{itemize}
	
\subsection{程序架构}
项目存放在 Project 文件夹下
\begin{itemize}
	\item 项目使用 Visual Studio 2019 编写，解决方案配置为 Release，编译平台为 x86；
	\item 程序使用 MFC 构建交互图形窗口；
	\item 使用 OpenGL 完成绘图操作；
	\item 通过 assimp 第三方库实现 3D 模型文件的导入；
	\item 自定义网格类和光照类实现模型绘制。
\end{itemize}
	
\subsection{构建思路}
两篇文献中两种变形算法的大致思想相同：
\begin{enumerate}
	\item 构建均匀平行六面体控制网格；
	\item 将待变形模型网格点部分或全部置入控制网格变形范围内，计算待变形点在控制网格中的相对坐标；
	\item 根据期望变形结果，改变控制点的分布；
	\item 应用不同基函数的变形算法，根据控制点坐标和待变形点的相对坐标计算待变形点的位置，获得变形后的模型；
\end{enumerate}

二种算法主要区别在于使用不同的变形基函数，而具有相同的算法流程。第三种直接变形的算法流程则有些许不同：
\begin{enumerate}
	\item 构建均匀平行六面体控制网格；
	\item 将待变形模型网格点部分或全部置入控制网格变形范围内，计算待变形点在控制网格中的相对坐标；
	\item 根据期望变形结果，直接移动网格点；
	\item 根据网格点的移动情况，反求新的控制点分布；
	\item 应用变形算法，根据控制点坐标和待变形点的相对坐标计算待变形点的位置，获得变形后的模型；
\end{enumerate}
因为三阶 B 样条基函数 FFD 变形下某一点的变形后坐标是关于 64 个控制点的欠定方程组，因此可以将所期望的变形后点的位置作为输入，利用广义逆计算得一组控制点的最小二乘解，根据计算结果改变控制点网格中对应点即可实现变形目标。
	
	
为了节约储存空间并提高计算效率，对于给定的网格数据结构和控制网格，我们首先考虑控制网格的范围，仅仅储存位于控制网格当中的网格点的索引。这样在绑定和变形时，可以快速地检索到网格中的点；另外，由于网格点相对于变形网格的坐标只需要计算一次，那么就可以单独提供一个绑定网格的函数接口，只在控制网格创建时调用，计算并储存这些坐标；最后提供一个用于变形的函数接口，在进行变形操作时调用此函数计算新的坐标。于是我们得到两个纯虚函数

\begin{lstlisting}
virtual void bind(HEMesh* mesh) = 0;		 // 绑定网格
virtual void deform(HEMesh* mesh) const = 0; // 变形函数
\end{lstlisting}
其中 bind 成员函数为模型绑定某一局部控制网格，计算位于局部控制网格内的待变形模型点的下标，并为选定点赋予新的局部坐标 (s,t,u)，作为变形参数；deform 成员函数变形模型，对 bind 函数所确定的选定点应用对应基函数变形算法变形。


在变形前需要移动控制点，变形后需要控制点坐标参与计算，因此还需要获得、修改控制点的方法
\begin{lstlisting}
point3f getCtrlPoint(int i, int j, int k) const;	// 获得控制点坐标
void setCtrlPoint(int i, int j, int k, point3f p);	// 修改控制点坐标
\end{lstlisting}
为了方便转换，我们储存的坐标值均通过线性映射转化为 [0,1]x[0,1]x[0,1] 下的坐标。


我们首先构建一个 Deformer 基类，储存变形所需的各类参数和变形函数接口。在此基础上根据变形所用的不同基函数（Bezier 基函数、B 样条基函数），继承出两种变形方法：BezierFFD 和 BsplineFFD，分别实现两种变形算法，其中
\begin{itemize}
	\item BezierFFD 继承类实现基于 Bezier 基函数的 FFD 变形；
	\item BSplineFFD 继承类实现基于三次 B 样条基函数的 FFD 变形，取均匀控制点与节点，并为最左最右侧节点赋予重数 4，以使得变形在网格边界处具有三阶几何连续性；
\end{itemize}
第三种直接变形方法也基于三次 B 样条基函数，并且和前两中算法过程存在重合，因此从 BsplineFFD 类中派生 DirectFFD 类，用于直接控制变形。

\subsection{使用说明}
在 final 文件夹下，打开 Project.exe 文件运行程序。显示界面左半部分负责控制模型绘制设置，以及移动被控制点。
\begin{itemize}
	\item 可以设置是否隐藏物体、网格和控制点；
	\item 通过线框模式 (LINE) 或者填充模式 (FILL) 显示模型；
	\item 通过滑块控件修改控制点的 (x,y,z) 坐标参数，根据当前控制网格的属性，会自动执行变形算法得到变形后的效果；
\end{itemize}
右半部分负责加载、保存模型、创建、删除网格以及设置变形模式。
\begin{itemize}
	\item 点击加载模型，可以打开 .dat, .obj 类型的文件，文件所在的路径必须完全为英文路径；
	\item 点击保存模型，可以将当前显示的模型保存为 .dat 类型的文件；
	\item 点击创建网格，会按照当前设置的网格参数和变形模式，创建一个控制网格。对于待变形的模型，如果控制网格与已经存在的其它控制网格会控制相同的模型点，或者控制网格当中不存在可以控制变形的模型点，则网格创建失败；
	\item 点击删除网格，会删除当前控制点对应的网格；
\end{itemize}
\begin{figure}[!htb]
	\centering
	\includegraphics[width=0.4\linewidth]{pic/base}
	\label{fig:base}
\end{figure}


例如加载圆柱模型 cylinder.dat 后，可以通过鼠标拖动画面从不同角度观察模型。创建控制网格后，网格点用红点表示，网格线为绿色，当前控制点为黄色。
\begin{figure}[!htb]
	\centering
	\includegraphics[width=0.3\linewidth]{pic/cylinder}\qquad
	\includegraphics[width=0.3\linewidth]{pic/cylinder-with-ctrl}
	\label{fig:cylinder}
\end{figure}


调整当前控制点参数后，模型便会发生变形。左图是 Beizer 基函数控制的变形，右图为直接控制变形
\begin{figure}[!htb]
	\centering
	\includegraphics[width=0.3\linewidth]{pic/cylinder-deform}\qquad
	\includegraphics[width=0.3\linewidth]{pic/cylinder-direct}
	\label{fig:cylinder-deform}
\end{figure}

\newpage
\section{测试分析}
\subsection{Beizer 变形}
测试 Beizer 基函数实现的变形效果。如图是对鹿头进行拉伸变形，可以看到变形效果非常光滑
\begin{figure}[!htb]
	\centering
	\includegraphics[height=0.3\linewidth,width=0.3\linewidth]{pic/02}\qquad
	\includegraphics[height=0.3\linewidth,width=0.3\linewidth]{pic/01}
	\label{fig:02}
\end{figure}

下面左图中我们对圆柱面选择不同的控制点变形，可以观察到变形结果的连续性；右图中设置了两个具有公共面的控制网格，分别对两个控制网格进行调整，得到一个 $ C^0 $ 连续的 FFD 和 $ C^1 $ 连续的 FFD 。对于右下图中的变形，在相邻面内侧共三排相邻顶点的共面性质产生了 $ C^1 $ 的光滑结果。
\begin{figure}[!htb]
	\centering
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/07}\qquad
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/common}\\[10pt]
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/09}\qquad
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/common-c0}\\[10pt]
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/11}\qquad
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/common-c1}\\[10pt]
	\label{fig:12}
\end{figure}

下图展示了对圆柱局部控制产生一个电话听筒耳的过程：先局部变形产生膨大的末端，然后施加全局弯曲。
\begin{figure}[!htb]
	\centering
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/03}\qquad
	\includegraphics[height=0.15\linewidth,width=0.4\linewidth]{pic/05}
	\label{fig:05}
\end{figure}


\subsection{B 样条变形}
如图所示是对一个花瓶进行 B 样条变形，其变形效果类似于 Beizer 基函数的变形。
\begin{figure}[!htb]
	\centering
	\includegraphics[width=0.3\linewidth]{pic/vase}\qquad
	\includegraphics[width=0.3\linewidth]{pic/vase-deform}
	\label{fig:vase}
\end{figure}
使用三次 B 样条基函数进行变形的优势是每次变形不需要计算全部点的参数，由于样条基函数的局部支撑性，只需要考虑与之相关的 64 个控制点的参数即可。例如在对如下有 11793 个顶点的场景进行变形时，Beizer 变形效率显著低于 B 样条变形：在 6x6x6 的控制网格下，前者变形一次需要约 2.89 秒，后者只需要 0.52 秒左右。
\begin{figure}[!htb]
	\centering
	\includegraphics[width=0.3\linewidth]{pic/scene}
	\label{fig:scene}
\end{figure}


\subsection{直接控制变形}
下图展示了对 B 样条曲面进行直接控制变形的效果，通过移动曲面上的一个点，控制点反过来随之变化，变化后的控制点又会对整个被控制的曲面部分进行变形。
\begin{figure}[!htb]
	\centering
	\includegraphics[height=0.3\linewidth,width=0.3\linewidth]{pic/14}\qquad
	\includegraphics[height=0.3\linewidth,width=0.3\linewidth]{pic/13}
	\label{fig:13}
\end{figure}

\newpage
\section{项目总结}
本次小组作业，我们组尝试完成了基于Bezier基和B样条基的三维模型自由变形(FFD)程序。通过此次合作编程，我们学习到了一种基础的三维模型变形算法，了解了算法关于变形高阶连续性、算法效率、基函数局限性等的性质，并知道了几种用以解决FFD方法部分局限性（如与控制点数量成正比的变形控制难度、无法将某一点精确移动至某一位置）的几种方法（修改基函数、利用广义逆矩阵反求控制点分布），并加以实现。在整个编程过程中，我们不仅学会了这一算法，更在实践的过程中接触了输出合适三维模型的方法，尝试了将简单模型变形为复杂目标物体的工艺，最后在整个实践过程中加深对算法以及其性质、适用性的掌握，收获颇丰。
	
\end{document}